#include "event_queue.h"
#include "myalloc.h"

#ifdef MAX_MALLOC_BLKS_NUM
#ifdef MALLOC_BLKS_NUM
#undef MALLOC_BLKS_NUM
#endif
#define MALLOC_BLKS_NUM EVQ_SIZE
#else
#ifndef MALLOC_BLKS_NUM
#define MALLOC_BLKS_NUM EVQ_SIZE
#else
#if MALLOC_BLKS_NUM > EVQ_SIZE
#undef MALLOC_BLKS_NUM
#define MALLOC_BLKS_NUM EVQ_SIZE
#endif
#endif
#endif

#define MALLOC_BLOCK_SIZE 64
#define MALLOC_POOL_SIZE (MALLOC_BLKS_NUM * MALLOC_BLOCK_SIZE)

#define MEM_INVALID	0
#define MEM_FREE	1
#define MEM_OCCUPIED	2
/* single producer multi consumer */
unsigned char spmc_pool[MALLOC_POOL_SIZE];

struct event_queue free_blk_queue;

void my_malloc_init(void)
{
    uint16_t i;
    unsigned char *p_buf;
    event_queue_init(&free_blk_queue);
    for (i = 0, p_buf = spmc_pool;
            i < MALLOC_BLKS_NUM; i++, p_buf += MALLOC_BLOCK_SIZE) {
        event_post(&free_blk_queue, MEM_FREE, (void *)p_buf);
    }
}

/* In this implementation parameter size is useless.
 * The returned value if either a pointer of a fixed-size
 * block or NULL.
 */
void *spmc_malloc(uint16_t size)
{
    uint32_t old_head = __sync_fetch_and_add(&(free_blk_queue.head), 1);
    struct event *memblk = EVENT(&free_blk_queue, old_head);
    if (__sync_bool_compare_and_swap(&(memblk->type), MEM_FREE, MEM_OCCUPIED))
        return memblk->arg;
    return 0;
}

uint8_t spmc_free(void *ptr_buf)
{
    if ((uint8_t *)ptr_buf < spmc_pool ||
            (uint8_t *)ptr_buf > spmc_pool + MALLOC_POOL_SIZE - MALLOC_BLOCK_SIZE)
        return 0;
    if (free_blk_queue.tail < free_blk_queue.head ||
	free_blk_queue.tail > free_blk_queue.head + EVQ_SIZE - 1)
	    return 0;
    struct event *memblk = EVENT(&free_blk_queue, free_blk_queue.tail);
    memblk->arg = ptr_buf;
    memblk->type = MEM_FREE;
    free_blk_queue.tail++;
    return 1;
}

